package com.itlong.cloud.sdkv3.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.itlong.cloud.POJO.DTO.property.IotCloudElevatorStateDTO;
import com.itlong.cloud.POJO.DTO.sdk.v3.CallElevatorDTO;
import com.itlong.cloud.POJO.VO.SDKMessageV3;
import com.itlong.cloud.POJO.VO.sdk.CloudControllerNotifyMessageVO;
import com.itlong.cloud.POJO.VO.sdk.v3.hw.*;
import com.itlong.cloud.constants.DeviceTypeConstant;
import com.itlong.cloud.constants.StepConstant;
import com.itlong.cloud.constants.TaskDeleteConstant;
import com.itlong.cloud.enumerate.ecode.BaseErrorCodeEnum;
import com.itlong.cloud.enumerate.ecode.SDKV3ErrorCodeEnum;
import com.itlong.cloud.sdkv3.service.ITaskService;
import com.itlong.cloud.utils.date.DateUtil;
import com.itlong.cloud.utils.tool.DeviceUtil;
import com.itlong.cloud.utils.words.StringHandlerUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class TaskServiceImpl implements ITaskService {

    private static final Logger LOG = LoggerFactory.getLogger(TaskServiceImpl.class);

    @Autowired
    RedisTemplate<String, String> redisTemplate;

    private long taskTimeout = 600000;//任务的时间，单位毫秒

    private long taskClearTimeSpan = 5000;//删除任务的消息有效时间，单位毫秒

    //指令下达的6秒锁
    @Override
    public String getElevatorLockKey(String deviceUnique) {
        return String.format("elevator_v3_lock_%s", deviceUnique);
    }

    //呼梯的短时锁
    @Override
    public boolean checkElevator(String deviceUnique, long n, long timeout) {
        String key = getElevatorLockKey(deviceUnique);
        long i = redisTemplate.opsForValue().increment(key, 1L);
        if (timeout > 0) {
            setExpire(key, timeout);
        }
        return i > n;
    }

    @Override
    public String getElevatorKey(String deviceUnique) {
        return String.format("elevator_v3_%s", deviceUnique);
    }

    //判断电梯是否有任务
    @Override
    public boolean hasElevatorKey(String deviceUnique) {
        String key = getElevatorKey(deviceUnique);
        return redisTemplate.hasKey(key);
    }

    @Override
    public Map<Object, Object> getElevatorData(String deviceUnique) {
        return redisTemplate.opsForHash().entries(getElevatorKey(deviceUnique));
    }

    @Override
    public void clearElevatorLockKey(String deviceUnique) {
        try {
            redisTemplate.delete(getElevatorLockKey(deviceUnique));
        } catch (Exception e) {

        }
    }

    @Override
    public void setElevatorData(String robotId, String deviceUnique, boolean isYQK) {
        String key = getElevatorKey(deviceUnique);
        HashMap<String, String> map = new HashMap<>();
        map.put("robotId", robotId);
        map.put("timestamp", DateUtil.getTimestampString());
        map.put("type", DeviceTypeConstant.YDTS);
        if (isYQK) {
            map.put("type", DeviceTypeConstant.YQKS);
        }
        redisTemplate.opsForHash().putAll(key, map);
        setExpire(key, taskTimeout);
    }

    @Override
    public String getTaskKey(String robotId) {
        return String.format("task_v3_%s", robotId);
    }

    @Override
    public boolean hasTaskKey(String robotId) {
        String key = getTaskKey(robotId);
        return redisTemplate.hasKey(key);
    }

    @Override
    public void setTaskData(CallElevatorDTO callElevatorDTO) {
        String key = getTaskKey(callElevatorDTO.getRobotId());
        String deviceUnique = callElevatorDTO.getDeviceUnique();
        boolean isYQK = DeviceUtil.isYQK(deviceUnique);
        HashMap<String, String> map = new HashMap<>();
        map.put("requestId", callElevatorDTO.getRequestId());//请求id
        map.put("robotId", callElevatorDTO.getRobotId());//机器人id
        map.put("fromFloor", callElevatorDTO.getFromFloor());//出发楼层
        map.put("openTime", callElevatorDTO.getOpenTime());//电梯在出发楼层和目的楼层的开门时长，单位秒
        map.put("toFloor", callElevatorDTO.getToFloor());//目的楼层
        map.put("timestamp", DateUtil.getTimestampString());//相当于创建时间
        map.put("updateTimestamp", DateUtil.getTimestampString());//记录更新的时间
        map.put("snr", callElevatorDTO.getSnr());//流水号
        map.put("step", "1");//乘梯阶段
        map.put("doorOpenTime", "0");//开门剩余时长，单位秒，可以不动态更新
        map.put("doorOpenTimeBaseTimestamp", "0");//剩余开门时长基于的时间戳
        if (isYQK) {
            map.put("controllerDeviceUnique", deviceUnique);//群控器设备号
            map.put("deviceUnique", "");//电梯设备号，哪一个电梯来接机器人
        } else {
            map.put("controllerDeviceUnique", "");//群控器设备号
            map.put("deviceUnique", deviceUnique);//电梯设备号，哪一个电梯来接机器人
        }
        map.put("floor", "0");//电梯当前所在楼层，0表示未知
        map.put("frontDoor", "8");//正门状态
        map.put("mode", "0");//电梯工作模式
        map.put("upDown", "8");//上下行状态
        map.put("list", "[]");//该任务下达的开关门指令数据，json数组
        map.put("delete", "0");//是否删除，只要被查询1次立马删除(0=否 1=是)
        map.put("iotDeviceId", callElevatorDTO.getIotDeviceId());//如果收不到派梯信息，通过内部的取消乘梯指令将任务取消掉
        redisTemplate.opsForHash().putAll(key, map);
        setExpire(key, taskTimeout);
    }

    @Override
    public void updateTaskData(String robotId, String hkey, String hvalue) {
        String key = getTaskKey(robotId);
        redisTemplate.opsForHash().put(key, hkey, hvalue);
    }

    @Override
    public Map<Object, Object> getTaskData(String robotId) {
        String key = getTaskKey(robotId);
        if (!hasKey(key)) {
            return null;
        }
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public boolean setExpire(String key, long timeout) {
        /*
            MILLISECONDS 毫秒
            MICROSECONDS 微秒
            NANOSECONDS 纳秒
        */
        return redisTemplate.expire(key, timeout, TimeUnit.MILLISECONDS);
    }

    /*
        step=2

        1.轿厢外发开门指令->延长开门
        2.轿厢外发关门指令->取消乘梯任务
        3.轿厢内发开门指令->不允许
        4.轿厢内发关门指令->确认行程

        step=4

        1.轿厢外发开门指令->不允许
        2.轿厢外发关门指令->取消乘梯任务
        3.轿厢内发开门指令->延长开门
        4.轿厢内发关门指令->不允许
    */
    @Override
    public SDKMessageV3 allowOpenDoor(String robotId, String deviceUnique, int effectiveTimeInt, String position, String requestId) {
        String key = getTaskKey(robotId);
        if (!hasKey(key)) {
            LOG.error("【sdkv3 allowOpenDoor 乘梯任务不存在】{} {} {} {} {}", robotId, deviceUnique, effectiveTimeInt, position, requestId);
            return new SDKMessageV3(SDKV3ErrorCodeEnum.TASK_NOT_EXIST.getErrorCode(), requestId);
        }
        Map<Object, Object> map = redisTemplate.opsForHash().entries(key);
        if (map != null && map.size() > 0 && map.containsKey("deviceUnique") && map.get("deviceUnique").toString().equals(deviceUnique)) {
            String step = map.get("step").toString();
            boolean b1 = step.equals(StepConstant.STRING_ARRIVE_START_FLOOR_ENTRY);
            boolean b2 = step.equals(StepConstant.STRING_ARRIVE_END_FLOOR_EXIT);
            //阶段判断
            if (!b1 && !b2) {
                return new SDKMessageV3(SDKV3ErrorCodeEnum.OPEN_DOOR_STEP_ERROR.getErrorCode(), requestId);
            }
            if (b1 && position.equals("in") && effectiveTimeInt > 0) {
                return new SDKMessageV3(SDKV3ErrorCodeEnum.COMMAND_OPEN_DOOR_ERROR.getErrorCode(), requestId);
            }
            if (b2 && position.equals("out") && effectiveTimeInt > 0) {
                return new SDKMessageV3(SDKV3ErrorCodeEnum.COMMAND_OPEN_DOOR_ERROR.getErrorCode(), requestId);
            }
            if (b2 && position.equals("in") && effectiveTimeInt == 0) {
                return new SDKMessageV3(SDKV3ErrorCodeEnum.COMMAND_OPEN_DOOR_ERROR.getErrorCode(), requestId);
            }
            //剩余开门时长是否足够的检查
            long doorOpenTime = Long.parseLong(map.get("doorOpenTime").toString());
            long doorOpenTimeBaseTimestamp = Long.parseLong(map.get("doorOpenTimeBaseTimestamp").toString());
            long span = (DateUtil.getTimestamp() - doorOpenTimeBaseTimestamp) / 1000;
            if (doorOpenTime - span <= 0) {
                return new SDKMessageV3(SDKV3ErrorCodeEnum.NO_DOOR_OPEN_TIME_ERROR.getErrorCode(), requestId);
            }
            return new SDKMessageV3(BaseErrorCodeEnum.SUCCESS.getErrorCode(), requestId);
        }
        return new SDKMessageV3(SDKV3ErrorCodeEnum.ALLOW_OPEN_DOOR_ERROR.getErrorCode(), requestId);
    }

    @Override
    public void huaWeiCallback(String deviceUnique, Map<String, Object> map) {
        String data = JSONObject.toJSONString(map.get("data"));
        Map<String, Object> dataMap = JSONObject.parseObject(data, HashMap.class);
        boolean isYDT = DeviceUtil.isYDT(deviceUnique);
        boolean isYQK = DeviceUtil.isYQK(deviceUnique);
        if (isYDT) {//云电梯运行状态上报
            IotCloudElevatorStateDTO elevator = JSONObject.parseObject(data, IotCloudElevatorStateDTO.class);
            updateTaskDataInner(elevator.getCustomData(), elevator.getTimestamp(), dataMap);
            //如果设备通知删除任务
            long timestamp = Long.parseLong(elevator.getTimestamp());
            long span = DateUtil.getTimestamp() - timestamp;
            if (
                    span <= taskClearTimeSpan &&
                            map.get("service_id").toString().equals("InfoService") &&
                            elevator.getCustomData() != null &&
                            !elevator.getCustomData().equals("") &&
                            elevator.getCustomData().contains("snr") &&
                            !elevator.getCustomData().contains("step")
            ) {
                LOG.info("【sdkv3设备端通知删除乘梯任务】{} {} {}", span, deviceUnique, map);
                TaskReportCustomData taskReportCustomData = JSONObject.parseObject(elevator.getCustomData(), TaskReportCustomData.class);
                notifyClearTask(TaskDeleteConstant.DEVICE_NOTIFY_YDT, taskReportCustomData);
            }
            //旧版的消息不包含机器人信息，也要处理
            //oldMsgHandle(deviceUnique, elevator, dataMap);
        } else if (isYQK) {
            CloudControllerNotifyMessageVO controller = JSONObject.parseObject(data, CloudControllerNotifyMessageVO.class);
            if (dataMap.containsKey("cmd") && dataMap.get("cmd").toString().equals("1003")) {//云群控器派梯信息上报
                String robotId = controller.getRobotId();
                String entryDeviceUnique = StringHandlerUtil.getElevatorDeviceUniqueBySn(controller.getSn());
                //将电梯和群控器进行映射，旧版的消息找到群控器->找到机器人信息->更新任务
                redisTemplate.opsForValue().set(getElevator2ControllerKey(entryDeviceUnique), String.format("%s,%s", deviceUnique, robotId), 86400000, TimeUnit.MILLISECONDS);
                if (hasTaskKey(robotId)) {
                    updateTaskData(robotId, "updateTimestamp", DateUtil.getTimestampString());
                    updateTaskData(robotId, "deviceUnique", entryDeviceUnique);
                }
            }
            //如果设备通知删除任务
            if (controller.getTimestamp() != null) {
                long timestamp = Long.parseLong(controller.getTimestamp());
                long span = DateUtil.getTimestamp() - timestamp;
                if (
                        span <= taskClearTimeSpan &&
                                map.get("service_id").toString().equals("InfoService") &&
                                controller.getCustomData() != null &&
                                !controller.getCustomData().equals("") &&
                                controller.getCustomData().contains("snr") &&
                                !controller.getCustomData().contains("step")
                ) {
                    LOG.info("【sdkv3设备端通知删除乘梯任务】{} {} {}", span, deviceUnique, map);
                    TaskReportCustomData taskReportCustomData = JSONObject.parseObject(controller.getCustomData(), TaskReportCustomData.class);
                    notifyClearTask(TaskDeleteConstant.DEVICE_NOTIFY_YQK, taskReportCustomData);
                }
            }
        }
    }

    //根据设备找群控器和机器人
    @Override
    public String getElevator2ControllerKey(String deviceUnique) {
        return String.format("elevator_v3_controller_%s", deviceUnique);
    }

    @Override
    public void addTaskListItem(String robotId, String item) {
        String key = getTaskKey(robotId);
        if (hasKey(key)) {
            String oldResult = redisTemplate.opsForHash().get(key, "list").toString();
            List<String> list = JSONObject.parseObject(oldResult, List.class);
            list.add(item);
            updateTaskData(robotId, "list", JSONObject.toJSONString(list));
        }
    }

    @Override
    public void clearTask(int reason, String robotId, String deviceUnique) {
        LOG.info("【sdkv3 clearTask】{} {} {}", reason, robotId, deviceUnique);
        String key = getElevator2ControllerKey(deviceUnique);
        //说明之前在操作云群控器
        if (hasKey(key)) {
            String[] temp = redisTemplate.opsForValue().get(key).split(",");
            String deviceUniqueController = temp[0];
            clear(reason, robotId, deviceUniqueController);
        }
        clear(reason, robotId, deviceUnique);
    }

    private void notifyClearTask(int reason, TaskReportCustomData taskReportCustomData) {
        List<TaskReport> list = taskReportCustomData.getRobot();
        if (list.size() > 0) {
            for (TaskReport taskReport : list) {
                if (taskReport.getSnr() == null || taskReport.getSnr().equals("")) {
                    continue;
                }
                String[] snrs = taskReport.getSnr().split("-");
                String requestId = snrs[0];
                String requestDeviceUnique = snrs[1];//向群控器还是云电梯预约的乘梯任务
                String elevatorKey = getElevatorKey(requestDeviceUnique);
                if (hasKey(elevatorKey)) {
                    Map<Object, Object> map = redisTemplate.opsForHash().entries(elevatorKey);
                    if (map != null && map.containsKey("robotId")) {
                        String robotId = map.get("robotId").toString();
                        Map<Object, Object> task = getTaskData(robotId);
                        if (task != null && task.containsKey("requestId") && task.get("requestId").toString().equals(requestId)) {
                            clear(reason, robotId, requestDeviceUnique);
                        }
                    }
                }
            }
        }
    }

    /**
     * 收到设备删除任务的报文标记删除(废弃)
     *
     * @param deviceUnique 设备编码
     */
    private void notifyClearTask111(String deviceUnique) {
        String elevatorKey = getElevatorKey(deviceUnique);
        String elevator2ControllerKey = getElevator2ControllerKey(deviceUnique);
        String robotId = "";
        if (hasKey(elevatorKey)) {
            Map<Object, Object> map = redisTemplate.opsForHash().entries(elevatorKey);
            if (map != null && map.containsKey("robotId")) {
                robotId = map.get("robotId").toString();
            }
        }
        if (hasKey(elevator2ControllerKey)) {
            String[] temp = redisTemplate.opsForValue().get(elevator2ControllerKey).split(",");
            robotId = temp[1];
        }
        if (!robotId.equals("") && hasTaskKey(robotId)) {
            Map<Object, Object> map = getTaskData(robotId);
            if (map != null && map.size() > 0) {
                updateTaskData(robotId, "delete", "1");
            }
        }
    }

    //将redis存的原始指令转成可以对外的格式
    @Override
    public List<String> commandForApi(List<String> list) {
        List<String> result = new ArrayList<>();
        for (String command : list) {
            HashMap<String, String> map = new HashMap<>();
            String requestId;
            String deviceUnique;
            String customData;
            String effectiveTime;
            String snr;
            String timestamp;
            if (command.indexOf("fOpenCtrl") > 0) {
                DoorCtrlOpen c = JSONObject.parseObject(command, DoorCtrlOpen.class);
                customData = c.getParas().getCustomData();
                effectiveTime = c.getParas().getFOpenCtrl();
                snr = c.getParas().getSnr();
                timestamp = c.getParas().getTimestamp();
            } else {
                DoorCtrlClose c = JSONObject.parseObject(command, DoorCtrlClose.class);
                customData = c.getParas().getCustomData();
                effectiveTime = c.getParas().getFOpenCtrl();
                snr = c.getParas().getSnr();
                timestamp = c.getParas().getTimestamp();
            }
            requestId = snr.substring(0, snr.length() - 14);
            deviceUnique = snr.substring(snr.length() - 13);
            DoorCtrlCustomData DoorCtrlCustomData = JSONObject.parseObject(customData, DoorCtrlCustomData.class);
            map.put("requestId", requestId);
            map.put("deviceUnique", deviceUnique);
            map.put("robotId", DoorCtrlCustomData.getRobot().getRobotId());
            map.put("effectiveTime", effectiveTime);
            map.put("position", DoorCtrlCustomData.getRobot().getRobotPosition());
            map.put("timestamp", timestamp);
            result.add(JSONObject.toJSONString(map));
        }
        return result;
    }

    private String getTaskRequestKey(String robotId) {
        return String.format("elevator_v3_request_%s", robotId);
    }

    @Override
    public boolean allowRequest(String robotId) {
        String key = getTaskRequestKey(robotId);
        if (hasKey(key)) {
            return false;
        }
        redisTemplate.opsForValue().set(key, "1", 1000, TimeUnit.MILLISECONDS);
        return true;
    }

    private void clear(int reason, String robotId, String deviceUnique) {
        try {
            LOG.info("【sdkv3 即将删除的机器人任务数据】{} {} {}", reason, getTaskData(robotId), deviceUnique);
            List<String> list = new ArrayList<>();
            list.add(getElevatorKey(deviceUnique));
            list.add(getTaskKey(robotId));
            list.add(getElevator2ControllerKey(deviceUnique));
            list.add(getElevatorLockKey(deviceUnique));
            redisTemplate.delete(list);
        } catch (Exception e) {
            LOG.error("【sdkv3 clear error】{}", e.getMessage());
        }
    }

    private void updateTaskDataInner(String data, String timestamp, Map<String, Object> dataMap) {
        if (data == null || data.equals("")) {
            return;
        }
        TaskReportCustomData customData = JSONObject.parseObject(data, TaskReportCustomData.class);
        for (TaskReport taskReport : customData.getRobot()) {
            String robotId = taskReport.getRobotId();
            if (hasTaskKey(robotId)) {
                if (taskReport.getSnr() == null || taskReport.getSnr().equals("")) {
                    continue;
                }
                String[] snrs = taskReport.getSnr().split("-");
                String requestId = snrs[0];
                Map<Object, Object> old = getTaskData(robotId);
                //需要匹配是同一个任务
                if (!old.get("requestId").toString().equals(requestId)) {
                    continue;
                }
                String oldStep = old.get("step").toString();
                String newStep = taskReport.getStep() + "";
                //网络延时会造成step突然变小
                if (Integer.parseInt(newStep) < Integer.parseInt(oldStep)) {
                    continue;
                }
                //阶段变了锁需要重新更新为taskTimeout
                if (!oldStep.equals(newStep)) {
                    if (hasElevatorKey(old.get("deviceUnique").toString())) {
                        setExpire(getElevatorKey(old.get("deviceUnique").toString()), taskTimeout);
                    }
                    if (hasElevatorKey(old.get("controllerDeviceUnique").toString())) {
                        setExpire(getElevatorKey(old.get("controllerDeviceUnique").toString()), taskTimeout);
                    }
                    setExpire(getTaskKey(robotId), taskTimeout);
                }
                updateTaskData(robotId, "updateTimestamp", DateUtil.getTimestampString());
                updateTaskData(robotId, "step", newStep);
                if (taskReport.getDoorOpenTime() != null && !taskReport.getDoorOpenTime().equals("")) {
                    updateTaskData(robotId, "doorOpenTime", taskReport.getDoorOpenTime());
                    updateTaskData(robotId, "doorOpenTimeBaseTimestamp", timestamp);
                }
                //
                if (dataMap.containsKey("floor")) {
                    updateTaskData(robotId, "floor", dataMap.get("floor").toString());
                    updateTaskData(robotId, "frontDoor", dataMap.get("frontDoor").toString());
                    updateTaskData(robotId, "mode", dataMap.get("mode").toString());
                    updateTaskData(robotId, "upDown", dataMap.get("upDown").toString());
                }
            }
        }
    }

    private void oldMsgHandle(String deviceUnique, IotCloudElevatorStateDTO elevator, Map<String, Object> dataMap) {
        if (elevator.getCustomData() != null) {
            return;
        }
        String key = getElevator2ControllerKey(deviceUnique);
        if (hasKey(key)) {
            String[] temp = redisTemplate.opsForValue().get(key).split(",");
            String robotId = temp[1];
            if (hasTaskKey(robotId)) {
                updateTaskData(robotId, "updateTimestamp", DateUtil.getTimestampString());
                updateTaskData(robotId, "floor", dataMap.get("floor").toString());
                updateTaskData(robotId, "frontDoor", dataMap.get("frontDoor").toString());
                updateTaskData(robotId, "mode", dataMap.get("mode").toString());
                updateTaskData(robotId, "upDown", dataMap.get("upDown").toString());
            }
        }
    }

    private boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    public CancelTravelWrap makeCancelTravelWrap(String requestId, String deviceId, String robotId, String snr) {
        CancelTravelData cancelTravelData = new CancelTravelData();
        cancelTravelData.setRobotId(robotId);
        //
        CancelTravelCustomData cancelTravelCustomData = new CancelTravelCustomData();
        cancelTravelCustomData.setCmd("cancelTravel");
        cancelTravelCustomData.setData(cancelTravelData);
        //
        CancelTravelParas cancelTravelParas = new CancelTravelParas();
        cancelTravelParas.setSourceFrom("itlong");
        cancelTravelParas.setSnr(snr);
        cancelTravelParas.setCmd("robot");
        cancelTravelParas.setTimestamp(DateUtil.getTimestampString());
        cancelTravelParas.setCustomData(JSONObject.toJSONString(cancelTravelCustomData));
        //
        CancelTravel cancelTravel = new CancelTravel();
        cancelTravel.setService_id("SettingService");
        cancelTravel.setCommand_name("dataUpdate");
        cancelTravel.setParas(cancelTravelParas);
        //
        CancelTravelWrap cancelTravelWrap = new CancelTravelWrap();
        cancelTravelWrap.setRequestId(requestId);
        cancelTravelWrap.setDeviceId(deviceId);
        cancelTravelWrap.setCancelTravel(cancelTravel);
        //
        return cancelTravelWrap;
    }

}